package hirt;

import java.nio.ByteBuffer;
import java.util.LinkedList;
import java.util.Vector;

/**
 * Bottom most class that all simulation items or entities derive from. All
 * items can contain a list of items (as an inventory). Each one has a unique
 * integer base id (identification) which is unique to a game session (per server).
 * Beside class type, each item has a sub type category, used to specify a
 * specific type of item of sed class (ie 'rubber' ball as oppose to 'cue' ball).
 * Items also have references to a possible item that contains it, called the
 * container. Also, in some situations, a ownership reference, owner, notes an
 * item's current owner (not necessarily something that the item contains).
 * @author puzzud
 */
public class Item
{
    /**
     * Unique (per Session/server) identification number.
     */
    int id;

    /**
     * Reference to a possible owner (Item) of this item.
     * (null if not owned).
     */
    Item owner;

    /**
     * Reference to a possible container (an item that contains this item as
     * its inventory) of this item.
     * (null if not contained).
     */
    Item container;

    /**
     * String sub categorial type of this class.
     */
    String type;

    /**
     * State of this item (can be used to indicate special states like arriving etc,
     * specific to this class or subclass.
     */
    int state;

    /**
     * Linked list of item inventory.
     */
    LinkedList<Item> itemList;

    /**
     * Default constructor (sets everything to null and -1).
     */
    public Item()
    {
        id = -1;
        owner = null;
        container = null;
        type = null;
        itemList = new LinkedList<Item>();

        state = -1;
    }

    /**
     * Constructor that sets the subclass and owner.
     * @param type String sub type of this item.
     * @param owner Desired reference to item owner.
     */
    public Item( String type, Item owner )
    {
        this();
        this.owner = owner;
        this.type = type;
    }

    /**
     * Builds an Item from a save string, presumably created by an item when saving to a file
     * this item should be identical to that one
     * @param saveString
     * @param items: Master Item List used to build the Item
     */
    public Item(String text, Vector<Item> items)
    {
        fromSaveString(text, items);
    }

    /**
     * Builds an Item from a byte stream, presumably created by an item across the network
     * this item should be identical to that one
     * @param bytes
     * @param items: Master Item List used to build the Item
     */
    public Item( ByteBuffer bytes, Vector<Item> items)
    {
        fromBytes(bytes,items);
    }

    /**
     * Builds an Item from a byte stream, presumably created by an item across the network
     * this item should be identical to that one
     * @param bytes
     * @param items: Master Item List used to build the action
     * @return what's left of the parem bytes after this method is completes
     */
    protected ByteBuffer fromBytes(ByteBuffer bytes, Vector<Item> items)
    {
        id = bytes.getInt(0);
        owner = ByteChanger.getItem(bytes, items, 4);

        container = ByteChanger.getItem(bytes, items, 8);
        if (container != null)
            container.putItem(this);
        
        state = bytes.getInt(12);
        //System.out.println("ERROR SPOT  length = " + bytes.limit());
        int length = bytes.getInt(16);
        type = ByteChanger.getString(bytes, 20, length);
        itemList = new LinkedList<Item>();
        try {
            bytes = ByteChanger.truncate(bytes, 20 + length);
        } catch (Exception ex) {

        }
        return bytes;
    }

    /**
     * Builds an Item from a save string, presumably read from a save
     * this item should be identical to the one saved in the file
     * @param save string
     * @param items: Master Item List used to build the action
     * @return what's left of the parem SaveString after this methods completes
     */
    protected String fromSaveString(String text, Vector<Item> items)
    {
        String substring = ByteChanger.getNextValue(text);
        id = Integer.parseInt(substring);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        owner = ByteChanger.getItem(substring, items);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        container = ByteChanger.getItem(substring, items);
        if (container != null)
            container.putItem(this);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        state = Integer.parseInt(substring);

        text = ByteChanger.incrementString(text);
        substring = ByteChanger.getNextValue(text);
        type = substring;

        itemList = new LinkedList<Item>();

        text = ByteChanger.incrementString(text);
        return text;
    }

    public int getId()
    {
        return( id );
    }

    public void setId( int id )
    {
        this.id = id;
    }
    
    public Item getOwner()
    {
        return( owner );
    }
    
    public void setOwner( Item owner )
    {
        this.owner = owner;
    }

    public String getType()
    {
        return( type );
    }

    public void setType( String type )
    {
        this.type = type;
    }

    public LinkedList<Item> getItemList()
    {
        return( itemList );
    }

    public void setItemList( LinkedList<Item> itemList )
    {
        this.itemList = itemList;
    }

    /**
     * Gets the linked list index of the item object in question.
     * @param item The item in question.
     * @return numerical index of item object in this item's inventory.
     */
    public int getItemIndex( Item item )
    {
        int index;
        boolean found = false;

        for( index = 0; index < itemList.size(); index++ )
        {
            if( itemList.get( index ) == item )
            {
                found = true;
                break;
            }
        }

        if( found == true )
        {
            return( index );
        }
        else
        {
            return( -1 );
        }
    }

    /**
     * Gets an actual item from this item's inventory by numerical index.
     * @param itemIndex numerical index of the item being sought out.
     * @return the actual item object.
     */
    public Item getItem( int itemIndex )
    {
        if( itemIndex >= itemList.size() )
        {
            return( null );
        }
        else
        {
            return( itemList.get( itemIndex ) );
        }
    }

    /**
     * Both returns and removes an item from this item's inventory by numerical index.
     * @param itemIndex numerical index of item to take.
     * @return item object being taken.
     */
    public Item takeItemByIndex( int itemIndex )
    {
        Item tempItem;

        if( itemIndex >= itemList.size() )
        {
            return( null );
        }
        else
        {
            tempItem = itemList.remove( itemIndex );
            return( tempItem );
        }
    }

    /**
     * Both returns and removes an item by the actual item reference.
     * @param item reference to the item to take.
     * @return the item taken.
     */
    public Item takeItem( Item item )
    {
        int index;
        boolean found = false;
        Item tempItem = null;

        for( index = 0; index < itemList.size(); index++ )
        {
            tempItem = itemList.get( index );

            if( tempItem == item )
            {
                itemList.remove(index);
                found = true;
                break;
            }
        }

        if( found == true )
        {
            return( tempItem );
        }
        else
        {
            return( null );
        }
    }

    /**
     * Remove the item by numerical index of this item's inventory.
     * @param itemIndex the numerical index of the item to remove.
     */
    public void removeItemByIndex( int itemIndex )
    {
        takeItemByIndex( itemIndex );
    }

    /**
     * Remove the item by actual item reference.
     * @param item reference to item to remove.
     */
    public void removeItem( Item item )
    {
        item.setContainer( null );
        takeItem( item );
    }

    /**
     * Add an item into this item's inventory. Noteworthy is that the container
     * of this added item is linked appropriately (better than just manually
     * adding this item to the itemList (inventory)).
     * @param item reference to item to add.
     */
    public void putItem( Item item )
    {
        if( itemList.indexOf( item ) < 0 )
        {
            itemList.add( item );
            item.setContainer( this );
        }
    }

    public int getStatus()
    {
        return state;
    }

    public void setStatus( int status )
    {
        this.state = status;
    }

    /**
     * Return the first occurrence item of sub type in this item's inventory.
     * (null return indicates nothing matching the criteria was found).
     * @param type sub type of item sought out.
     * @return actual item found (if found, otherwise null).
     */
    public Item getFirstItemByType( String type )
    {
        // Pretty raw approach. TODO: FIX AND MAKE BETTER!
        int index;

        for( index = 0; index < itemList.size(); index++ )
        {
            Item tempItem = itemList.get( index );

            if( tempItem.getType().equals( type ) )
            {
                return( tempItem );
            }
        }

        return( null );
    }

    /**
     * Get a runtime generated list of items in this item's inventory that match
     * the specified sub type.
     * UNTESTED
     * @param type sub type of items to return.
     * @return Vector<Item> of items with sub type.
     */
    public Vector<Item> getListOfItemsByType( String type )
    {
        Vector<Item> itemByTypeList;

        if( getItemCountByType( type ) == 0 )
        {
            return( null );
        }
        else
        {
            itemByTypeList = new Vector<Item>();

            // Iterate through list and find each item of this type, add it to temporary list.
            int index;
            for( index = 0; index < itemList.size(); index++ )
            {
                Item tempItem = itemList.get( index );
                
                if( tempItem instanceof Item2D )
                {
                    Item2D tempItem2D = ( Item2D )tempItem;
                    
                    if( tempItem2D.getType().equals( type ) )
                    {
                        itemByTypeList.add( tempItem );
                    }
                }
            }

            return( itemByTypeList );
        }
    }

    /**
     * Return the number of items in this items inventory of the specified
     * sub type.
     * @param type sub type of item to count.
     * @return count of number of items matching specified sub type.
     */
    public int getItemCountByType( String type )
    {
        int count, index;

        for( index = 0, count = 0; index < itemList.size(); index++ )
        {
            Item tempItem = itemList.get( index );

            if( tempItem instanceof Item2D )
            {
                Item2D tempItem2D = ( Item2D )tempItem;

                if( tempItem2D.getType().equals( type ) )
                {
                    count++;
                }
            }
        }

        return( count );
    }

    public Item getContainer()
    {
        return( container );
    }

    public void setContainer( Item container )
    {
        this.container = container;
    }

    public int getState()
    {
        return( state );
    }

    public void setState( int state )
    {
        this.state = state;
    }

    @Override
    public String toString()
    {
        return type;
    }

    /**
     * Returns a String representation of this Item
     * Used to save this item to a file
     * @return Save String
     */
    public String toSaveString()
    {
        String text = "";
        text += id + "\t";
        text += ByteChanger.itemToByte(owner) + "\t";
        text += ByteChanger.itemToByte(container) + "\t";
        text += state + "\t";
        text += type + "\t";
        return text;
    }

    /**
     * Clone code (not used or tested currently).
     * @return a cloned copy of this item.
     */
    @Override
    public Object clone()
    {
        Item copiedItem = new Item();
        copiedItem.setId(id);
        copiedItem.setOwner(owner);
        copiedItem.setStatus(state);
        copiedItem.setType(type);
        copiedItem.setItemList(itemList);

        return copiedItem;
    }

    /**
     * Returns a ByteBuffer representation of this Item
     * Used to send items over the network
     * @return ByteBuffer
     */
    public ByteBuffer toByte() {
        ByteBuffer buf = ByteBuffer.allocate(90);
        buf.putInt(id);
        buf.putInt(ByteChanger.itemToByte(owner));
        buf.putInt(ByteChanger.itemToByte(container));
        buf.putInt(state);
        buf.putInt(type.getBytes().length);
        buf.put(type.getBytes());
        return buf;
    }



}
